function tooltips()
{
  var tooltip = document.createElement(tooltip);
  var timeout = 0;
  var ele = document.getElementsByClassName('tooltip');
  
  tooltip.id = 'tooltip';
  document.body.appendChild(tooltip);
  
  // go backwards to avoid dynamic list being harmful
  for (var i=ele.length-1; i>=0; i--)
  {
    ele[i].onmouseover = function(e)
    {
      var self = this;
      self.onmousemove = function(e2)
      {
        e = e2;
      };
      clearTimeout(timeout);
      timeout = setTimeout(function()
      {
        tooltip.style.display = 'block';
        tooltip.innerHTML = self.getAttribute('tooltip');
        document.onmousedown = 
        document.onmouseover = self.onmouseout = document.onmouseout = function() // all three of these events will trigger when needed, just making extra sure
        {
          tooltip.style.display = 'none';
        };
        self.onmousemove = function(e)
        {
          var x = e.clientX+13;
          if (window.innerWidth-tooltip.offsetWidth < x)
          {
            x = e.clientX - tooltip.offsetWidth - 5;
          }
          tooltip.style.top = e.clientY+5 + 'px';
          tooltip.style.left = x + 'px';
        };
        self.onmousemove(e);
      }, 400);
      self.onmouseout = function()
      {
        clearTimeout(timeout);
      };
    }
    ele[i].removeClass('tooltip');
  }

}

function ui()
{
  tooltips();
  var self = this;
  var lastDevice = widget.preferenceForKey('lastDevice');
  
  this.device = { // units are all integer pixels unless specified
    settings: {
      loaded: false
      /*
        cpy: (int: minimum allowed setInterval or setTimeout time),
        connection: (int: bits per second for xhr),
        storage: (int: bytes available for preferences)
        loaded: (boolean: whether or not the device is loaded)
        barsize: (imt: size of the bar at the bottom of the screen)
      */
    },
    media: {
	/* type etc */
    },
    screen: {
      /* width, height, availWidth, availHeight */
    },
    widget: {
      /*
      width,
      height,
      top,
      left,
      defaultWidth,
      defaultHeight,
      opened (boolean: step1 - if widget iframe has been added to dom),
      began (boolean: step2 - once the index.html file loads),
      loaded (boolean: step3 - if widget's onload event has fired),
      preferences (object),
      storage (int bytes used),
      mode,
      dockable,
      dockX,
      dockY
      */
    }
  }
  
  // "e_" signifies element reference
  var e_device = g('device');
  var e_screen = g('screen');
  var e_panel = g('panel');
  var e_status = g('panel-status');
  var e_custom = g('panel-custom');
  var e_desktop = g('desktop');
  var e_screenContent = g('screen-content');
  var e_bar= g('bar');
  
  var knownWidth;
  var knownHeight;

  var minWidth = 0;
  this.alert = function(markup) // markup is either a string or a dom element
  {
    var winHeight = window.innerHeight;
    
    g('dialogue').style.marginLeft = self.device.settings.loaded ? '100px' : '0';
    g('dialogue').style.top = Math.max(0,Math.round(winHeight*0.4-85))+'px';
    minWidth = 500;
    markup = createHTML(markup);
    
    g('dialogue').innerHTML = '';
    g('dialogue').appendChild(markup);
    g('dialogue').style.display = 'block';
    g('screen-blocker').style.display = 'block';
    
    document.body.addClass('dialogue');
    
    self.resizeWindow(0,170);
    
    applyGlobalDisable(true, g('dialogue'));
    // focus the submit button
    var input = g('dialogue').getElementsByTagName('input');
    for (var i=0; i<input.length; i++)
    {
      if (input[i].type.toUpperCase() == 'TEXT')
      {
        input[i].focus();
        input[i].select();
        return;
      }
    }
    for (var i=0; i<input.length; i++)
    {
      if (input[i].type.toUpperCase() == 'SUBMIT')
      {
        input[i].focus();
        return;
      }
    }

    
  }
  this.alertClose = function()
  {
    minWidth = 0;
    g('dialogue').innerHTML = '';
    g('dialogue').style.display = '';
    g('screen-blocker').style.display = '';
    self.resizeWindow();
    applyGlobalDisable(false);
    self.resizeWindow();
    document.body.removeClass('dialogue');
    return false;
  }
  
  // if the emulated widget doesnt have the special script tag then any resizewindow it calls will effect the real window
  var screenInterval = setInterval(function(){
    if (window.innerWidth != knownWidth || window.innerHeight != knownHeight)
    {
      knownWidth = window.innerWidth;
      knownHeight = window.innerHeight;
      self.failWidget('script');
    }
  },500);
  
  this.failWidget = function(type)
  {
    self.closeWidget();
    g('desktop').addClass('error');
    g('desktop-error').className = type;
    
    var count = 0;
    var interval = setInterval(function()
    {
      if (count++>4) { clearInterval(interval); }
      g('desktop-error')[count%2?'addClass':'removeClass']('flash');
    },200);
  }
  
  this.setIcon = function(src)
  {
    var e_icon = g('info-icon');
    var DEFAULT = 'emulator_files/img/widget-icon.png';
    
    if (!src)
    {
      e_icon.setAttribute('src', DEFAULT);
    }
    
    src = 'widget/'+src;
    
    var pic = new Image();
    pic.onerror = function()
    {
      e_icon.setAttribute('src', DEFAULT);
    };
    pic.onload = function()
    {
      e_icon.setAttribute('src', src);
    };
    pic.src = src;
  }
  
  this.resizeWindow = function(minW,minH)
  {
    var width = Math.max(minWidth, (self.device.screen.width+60||0) + e_panel.offsetWidth);
    var height = Math.max((self.device.screen.height+60||0), e_panel.offsetHeight+30);
    
    height += this.device.settings.loaded && g('info').offsetHeight;
    
    if (width < minW) width = minW;
    if (height< minH) height= minH;
    
    if (knownWidth != width || knownHeight != height)
    {
      window.resizeTo(knownWidth = width, knownHeight = height);
    }
    else
    {
      window.resizeBy(0, 0);
    }
  }
  
  this.closeWidget = function()
  {
    if (ui.device.widget.opened)
    {
      emulator.closeWidget();
      g('frameWrapper').innerHTML = '';
      e_screenContent.style.display = '';
      e_desktop.style.display = '';
      scrollbars.fix();
      self.updateStatus();
    }
  };
  
  this.reloadWidget = function()
  {
    emulator.launchWidget();
    return false;
  };
  
  this.dockWidget = function()
  {
    if (ui.device.widget.loaded)
    {
      emulator.toggleDocked();
    }
  };
  
  /**
   * Selector for device button
   *
   * @returns Element A reference to the dom element
   */
  
  function getDeviceButtonByUniqueId(id)
  {
    var buttons = g('panel-devices').getElementsByTagName('button');
    for (var i=0; i<buttons.length; i++)
    {
      if (buttons[i].getAttribute('uniqueId') == id)
      {
        return buttons[i];
      }
    }
    return null;
  }
  
  this.closeDevice = function()
  {
    self.closeWidget();
    e_device.style.display = 'none';
    panelDeviceFocus(null);
    self.updateStatus();
    this.device = {settings:{loaded:false},screen:{},widget:{}}
    self.resizeWindow();
  };
  
  var status_showStorage = false;
  this.updateStatus = function()
  {
    var str = '';
    var e_info = g('info');
    var e_infoStats = g('info-stats');
  
    if (self.device.settings.loaded)
    {
      var connection = self.device.settings.connection;    
      connection = connection > 0 ? (connection/1024)+' Kbps': 'Default';
      
      g('status-media').innerText      = self.device.media.type;
      g('status-screen').innerText     = self.device.screen.width + ' x ' + self.device.screen.height;
      g('status-connection').innerText = connection;
      g('status-storage').innerText    = self.device.settings.storage.toBytes();
      g('status-dock').innerText       = (!self.device.settings.dockX ? 'Not enabled' : (self.device.settings.dockX + ' x ' + self.device.settings.dockY));
      
      if (self.device.widget.opened)
      {
        var storage = 100*self.device.widget.storage/self.device.settings.storage;
        storage = storage>0&&storage<1 ? '< 1%' : Math.floor(storage)+'%';
        
        var str = '\
          \u25AA Mode: "'+self.device.widget.mode+'"<br>\
          \u25AA Size: '+self.device.widget.width + ' x ' + self.device.widget.height+'<br>\
          \u25AA Position: '+(self.device.widget.mode=='docked' ? 'NA' : (self.device.widget.left + ' x ' + self.device.widget.top))+'<br>\
          \u25AA Storage: '+storage+'\
        ';
        
        if (!status_showStorage)
        {
          str += ' <a href="#" id="storage_moreless">[more]</a>';
        }
        else
        {
          str += ' <a href="#" id="storage_moreless">[less]</a>';
          
          var str_pref = '';
          for (var i in self.device.widget.preferences)
          {
            if (typeof self.device.widget.preferences[i] == 'string')
            {
              str_pref += '\u25CF ' + i + ' = ' + self.device.widget.preferences[i] + '<br>';
            }
          }
          str += str_pref ? '<pre>'+str_pref.safe()+'</pre> <input type="button" style="cursor:pointer;" value="Clear Storage" id="clearStorage">' : '<pre>Using no storage</pre>';
        }
        e_infoStats.innerHTML = str;
        e_info.style.display = 'block';
      }
      else
      {
        e_infoStats.innerHTML = '';
        e_info.style.display = 'none';
      }
      
      if (g('clearStorage')) g('clearStorage').onclick = function()
      {
        emulator.clearPreferences();
        self.updateStatus();
      };
      if (g('storage_moreless')) g('storage_moreless').onclick = function()
      {
        status_showStorage = !status_showStorage;
        self.updateStatus();
      };
    }
    else
    {
      showStorage = false;
    }
    self.resizeWindow();
  };
  
  self.updateStatus();
  
  function selectColor(col)
  {
    e_screen.style.backgroundColor = col;
    widget.setPreferenceForKey(col, 'desktopColor');
  }
  selectColor(widget.preferenceForKey('desktopColor') || '#69A7A6');
  
  var colourButtons = document.getElementById('desktop-colours').getElementsByTagName('button');
  
  colourButtons[0].onclick = colourButtons[0].onmousedown = function(){selectColor('#A85151')};
  colourButtons[1].onclick = colourButtons[1].onmousedown = function(){selectColor('#697FA7');};
  colourButtons[2].onclick = colourButtons[2].onmousedown = function(){selectColor('#69A7A6');};
  colourButtons[3].onclick = colourButtons[3].onmousedown = function(){selectColor('#537452');};
  colourButtons[4].onclick = colourButtons[4].onmousedown = function(){selectColor('#BEB060');};
  colourButtons[5].onclick = colourButtons[5].onmousedown = function(){selectColor('#FFFFFF');};
  colourButtons[6].onclick = colourButtons[6].onmousedown = function(){selectColor('#888888');};
  colourButtons[7].onclick = colourButtons[7].onmousedown = function(){selectColor('#000000');};
  
  g('close').onclick = function()
  {
    window.close();
  };
  
  g('config').onclick = function()
  {
    document.body.toggleClass('config');
    self.resizeWindow();
  };
  
  g('help').onclick = function()
  {
    widget.openURL(g_helpURL);
  };
  
  g('icons-launchwidget').onclick   = self.reloadWidget;
  g('desktop-error-launch').onclick = self.reloadWidget;
  
  g('info-close').onclick = self.closeWidget;
  
  g('info-reload').onclick = self.reloadWidget;
  
  g('info-dock').onclick = self.dockWidget;
  
  /********************************/
  /* begin custom device ui stuff */
  /********************************/
  
    
  g('device-new').onclick = function() // [custom]
  {
    if (this.disabled) { return; }
    
    if (e_custom.style.display == 'block')
    {
      e_custom.style.display = '';
      return;
    }
    
    e_custom.style.display = 'block';
    g('panel-new-container').appendChild(e_custom);
    
    g('custom-id').value = '';
    
    var e_name = g('custom-name');
    var name = "";
    var count = 0;
    
    // create string like "New Device 3", so first
    // find out what number to place at the end:
    do {
      count++;
      name = "New Device" + (count>1 ? ' '+count : '');
      for (var i=0; i<g_devices.length; i++)
      {
        if (g_devices[i][1] == name) break;
      }
    } while (i < g_devices.length)
    
    e_name.value = name;
    e_name.focus()
    e_name.select();
    
    g('custom-media').options.selectedIndex = parseInt(widget.preferenceForKey('custom-media')) || 0;
    g('custom-screen-x').value = widget.preferenceForKey('custom-screen-x') || '640';
    g('custom-screen-y').value = widget.preferenceForKey('custom-screen-y') || '480';
    g('custom-connection').options.selectedIndex = parseInt(widget.preferenceForKey('custom-connection')) || 0;
    g('custom-storage').options.selectedIndex = parseInt(widget.preferenceForKey('custom-storage')) || 0;
    
    var dock = !widget.preferenceForKey('custom-dock');
    
    var e_dockX = g('custom-dock-x');
    var e_dockY = g('custom-dock-y');
    
    e_dockX.value = widget.preferenceForKey('custom-dock-x') || '48';
    e_dockY.value = widget.preferenceForKey('custom-dock-y') || '48';
    
    g('custom-dock').checked = dock;
    e_dockX.disabled = !dock;
    e_dockY.disabled = !dock;
    
    self.resizeWindow();
  }
  
  g('custom-screen-x').onchange = function()
  {
    var str = (parseInt(this.value.filterNumbers())||0).toRange(200,800);
    if (this.value != str)
    {
      highlightInput(this);
      this.value = str;
    }
  };
  
  g('custom-screen-x').onchange = restrict;
  g('custom-screen-y').onchange = restrict;
  g('custom-dock-x').onchange = restrict;
  g('custom-dock-y').onchange = restrict;
  
  function restrict()
  {
    var str = ''
    var el = null;
    var err = false;
    
    el = g('custom-screen-x');
    str = (parseInt(el.value.filterNumbers())||0).toRange(200,800);
    if (el.value != str)
    {
      highlightInput(el);
      el.value = str;
      err = true;
    }

    el = g('custom-screen-y');
    str = (parseInt(el.value.filterNumbers())||0).toRange(200,600);
    if (el.value != str)
    {
      highlightInput(el);
      el.value = str;
      err = true;
    }
    
    if (g('custom-dock').checked)
    {
      el = g('custom-dock-x');
      str = (parseInt(el.value.filterNumbers())||0).toRange(16,400);
      if (el.value != str)
      {
        highlightInput(el);
        el.value = str;
        err = true;
      }
      
      el = g('custom-dock-y');
      str = (parseInt(el.value.filterNumbers())||0).toRange(16,400);
      if (el.value != str)
      {
        highlightInput(el);
        el.value = str;
        err = true;
      }
    }
    
    return err;
  };
  
  g('custom-dock').onchange = function()
  {
    g('custom-dock-x').disabled = !this.checked;
    g('custom-dock-y').disabled = !this.checked;
  };
  
  function highlightInput(el)
  {
    var count = 16;
    el.style.color = 'red';
    el.addEventListener('focus', focus, false);
    e_custom.doNotSubmit = true;
    var interval = setInterval(function()
    {
      if (count < 12) { e_custom.doNotSubmit = false; }
      if (--count<1)
      {
        el.style.color = '';
        clearInterval(interval);
        el.removeEventListener('focus', this, false);
      }
      else
      {
        el.style.color = '#' + count.toString(16) + 0 + 0;
      }
    }, 200);
    function focus()
    {
      this.style.color = '';
      clearInterval(interval);
      this.removeEventListener('focus', this, false);
    }
  }
  

  
  // custom OK
  e_custom.onsubmit = function()
  {
    var uniqueId = g('custom-id').value;
    var name = g('custom-name').value;
    var media = g('custom-media').options.selectedIndex || 0;
    var screenX = g('custom-screen-x').value;
    var screenY = g('custom-screen-y').value;
    var framerate = 0; // g('custom-framerate').options.selectedIndex || 0;
    var connection = g('custom-connection').options.selectedIndex || 0;
    var storage = g('custom-storage').options.selectedIndex || 0;
    var dock = g('custom-dock').checked;
    var dockX = g('custom-dock-x').value;
    var dockY = g('custom-dock-y').value;
    
    if (restrict() || e_custom.doNotSubmit)
    {
      g('custom-ok').focus();
      return;
    }
    screenX = parseInt(screenX);
    screenY= parseInt(screenY);
    
    var newDevice = !uniqueId;
    
    if (newDevice) { uniqueId = 'random'+Math.random(); }
    
    widget.setPreferenceForKey(''+media,'custom-media');
    widget.setPreferenceForKey(screenX,'custom-screen-x');
    widget.setPreferenceForKey(screenY,'custom-screen-y');
    widget.setPreferenceForKey(framerate,'custom-framerate');
    widget.setPreferenceForKey(''+connection,'custom-connection');
    widget.setPreferenceForKey(''+storage,'custom-storage');
    widget.setPreferenceForKey( dock ? '' : '0', 'custom-dock');
    if (dock)
    {
      widget.setPreferenceForKey(dockX, 'custom-dock-x');
      widget.setPreferenceForKey(dockY, 'custom-dock-y');
    }
    
    media = g('custom-media').options[media].value;
    
    framerate = 0; //parseInt(g('custom-framerate').options[framerate].value);
    connection = parseInt(g('custom-connection').options[connection].value);
    storage = parseInt(g('custom-storage').options[storage].value);
    
    // they can only both be zero OR both be non-zero
    dockX = dock ? Math.max(16, parseInt(dockX) || 48) : 0;
    dockY = dock ? Math.max(16, parseInt(dockY) || 48) : 0;
    
    if (dockX > screenX) dockX = screenX;
    if (dockY > screenY) dockY = screenY;
    
    var settings = [
      uniqueId,
      name,
      media,
      screenX,
      screenY,
      framerate,
      connection,
      storage,
      dockX,
      dockY
    ];
    
    
    if (!newDevice) // editing existing device
    {
      for (var i=0; i<g_devices.length; i++)
      {
        if (g_devices[i][0]==uniqueId)
        {
          g_devices[i] = settings;
          break;
        }
      }
    }
    
    if (newDevice || i==g_devices.length) // new device
    {
      uniqueId = settings[0];
      g_devices.push(settings);
      g('panel-devices').appendChild(createButtonHTML(settings));
    }
    
    self.resizeWindow();    
    
    var button = getDeviceButtonByUniqueId(uniqueId);;
    button.innerText = name;
    
    loadDevice(settings);
    
    widget.setPreferenceForKey(g_devices, 'devices');
    
    window.applyGlobalDisable(false);
    
    return false;
  };
  // custom CANCEL
  document.getElementById('custom-cancel').onclick = function()
  {
    e_custom.style.display = 'none';
    if (self.device.settings.loaded)
    {
      e_status.style.display = 'block';
    }
    window.applyGlobalDisable(false);
    self.resizeWindow();
  };
  
  
  var panelDeviceThis;
  function panelDeviceFocus(ele) // if (!e) then blur nothing, if (e==-1) then focus last buttong, else focus e
  {
    e_status.style.display = 'block';
    e_custom.style.display = 'none';    
    
    if (ele)
    {
      if (panelDeviceThis)
      {
        panelDeviceThis.className = '';
      }
      ele.className = 'chosen';
      ele.parentNode.insertBefore(e_status, ele);
      ele.parentNode.insertBefore(ele, e_status); // doing this twice is equiv. to insertAfter
    }
    else // blur buttons
    {
      if (panelDeviceThis)
      {
        panelDeviceThis.className = '';
      }
      e_status.style.display = 'none';
    }
    panelDeviceThis = ele;
  }  

  function createButtonHTML(settings)
  {
    return createHTML([
      ['button',
        'type','button',
        'uniqueId', settings[0],
        'onclick', deviceOnclick,
        settings[1]
      ]
    ]);
  };
    
  function deviceOnclick(evt)
  {
    if (this==panelDeviceThis || this.disabled) return;
    
    var id = this.getAttribute('uniqueId');
    
    for (var i=0; i<g_devices.length; i++)
    {
      if (g_devices[i][0] == id)
      {
        loadDevice(g_devices[i]);
        return;
      }
    }
  }
    
  function loadDevice(settings)
  {
    g('device-delete').disabled = false;
    
    g('panel-devices-none').style.display = 'none';
    lastDevice = settings[0];
    panelDeviceFocus(getDeviceButtonByUniqueId(lastDevice));
    widget.setPreferenceForKey(lastDevice, 'lastDevice'); // remember last device
    emulator.loadDevice(settings[2],settings[3],settings[4],settings[5],settings[6],settings[7],settings[8],settings[9],true);
  }

  /**
   * Creates buttons for saved devices
   *
   * This will also load the last used device, or
   * the first device in the list
   */
  
  
  this.generateDevices = function()
  {
    var ele = null;
    var deviceToLoad = null;
    
    var area = g('panel-devices');
    
    for (var i=0; i<g_devices.length; i++)
    {
      ele = createButtonHTML(g_devices[i]);
      area.appendChild(ele);
      
      if (i==0 || g_devices[i][0] == lastDevice)
      {
        deviceToLoad = ele;
      }
    }
    tooltips();
    loadLastDevice();
    self.resizeWindow();
  }

  g('panel-devices-restore').onclick = function()
  {
    g_devices = g_devicesDefault.concat(); // make copy
    widget.setPreferenceForKey(g_devices,'devices');
    self.generateDevices();
  };

  function loadLastDevice()
  {
    if (!g_devices.length) { return; }
    
    // load last device
    for (var i=0; i<g_devices.length; i++)
    {
      if (g_devices[i][0] == lastDevice)
      {
        break;
      }
    }
    if (i==g_devices.length) // load the first device since we can't find the last
    {
      i = 0;
      lastDevice = g_devices[0][0];
    }
    loadDevice(g_devices[i]);
  }
  
  g('device-delete').onclick = function()
  {
    var delButton = this;
    
    for (var i=0; i<g_devices.length; i++)
    {
      if( g_devices[i][0] == lastDevice)
      {
        break;
      }
    }
    if (i==g_devices.length) { return; }
    
    self.alert(
      ['form',
        'onsubmit',function()
        {
          g_devices.splice(i,1);
          widget.setPreferenceForKey(g_devices,'devices');
          
          var button = g('panel-devices').getElementsByTagName('button')[i]
          
          self.closeDevice();
          
          button.parentNode.removeChild(button);
          
          self.alertClose();
          
          //open a new device
          if (g_devices.length)
          {
            loadDevice(g_devices[Math.min(g_devices.length-1, i)]);
          }
          else
          {
            delButton.disabled = true;
            g('panel-devices-none').style.display = 'block';
          }
          
          return false;
        },
        ['div',
          ['h4',
            'Confirm device removal'
          ],
          'Are you sure you want to remove the device called: "'+g_devices[i][1]+'"?',
          ['br'],['br'],
          ['b','Note: '],
          'Common device settings can be found in the ',['a','href',g_helpURL,'help'],'.'
        ],
        ['fieldset',
          ['input',
            'type','submit',
            'value','Remove',
          ],
          ['input',
            'type','button',
            'value','Cancel',
            'onclick',function()
            {
              self.alertClose();
            }
          ]
        ]
      ]
    );
  };
  
  g('status-edit').onclick = function()
  {
    
    /* resolve the settings to load */
    
    var settings = null;
    for (var i=0; i<g_devices.length; i++)
    {
      if (g_devices[i][0] == lastDevice) { break; }
    }
    if (i==g_devices.length) { return; }
    settings = g_devices[i];
    
    /* show the form */
    
    g('panel-devices').insertBefore(g('panel-custom'), g('panel-status'));
  
    g('panel-custom').style.display = 'block';
    g('panel-status').style.display = 'none';
    
    self.resizeWindow();
    
    /* disable other elements */
    
    window.applyGlobalDisable(true, g('panel-custom'), g('panel-main'))
    
    /* apply element values */
    
    g('custom-id').value = settings[0];
    
    var e_name = g('custom-name');
    
    e_name.value = settings[1];
    e_name.focus()
    e_name.select();
    
    selectByValue(g('custom-media'), settings[2]);
    g('custom-screen-x').value = settings[3];
    g('custom-screen-y').value = settings[4];
    selectByValue(g('custom-connection'), settings[6]);
    selectByValue(g('custom-storage'), settings[7]);
    
    function selectByValue(sel, value)
    {
      for (var i=0; i<sel.options.length; i++)
      {
        if (sel[i].value == value) { break; }
      }
      sel.options.selectedIndex = i==sel.options.length ? 0 : i; // 'i' will be 0 if there was no find
    }
    
    var e_dockX = g('custom-dock-x');
    var e_dockY = g('custom-dock-y');
    
    e_dockX.value = settings[8] || '48';
    e_dockY.value = settings[9] || '48';
    
    var dock = !!settings[8];
    g('custom-dock').checked = dock;
    e_dockX.disabled = !dock;
    e_dockY.disabled = !dock;
  };
  
  
  
};